#pragma once
#include <future>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <stdexcept>

/**
 *                                  *************************
 *                                  *       线程池工具        *
 *                                  *************************
 * 用例：
 * 构造线程池
 * ThreadPoolTool t(4);
 *
 * 提匿名函数任务
 * std::future<int> res1= t.enqueue([](int a,int b)->int {
 *    return a+b;
 * },2,3);
 *
 *输出结果：5
 *std::cout << res1.get() << std::endl;
 *
 *class Calculator {
 *public:
 *Calculator() {}
 *int add(int a,int b) { return a+b; }
 *};
 *
 *Calculator calc;
 *
 *提交类成员变量
 *std::future<int> res2 = t.enqueue(
 *std::bind(&Calculator::add,&calc,std::placeholders::_1,std::placeholders::_2),
 *1,6);
 *
 *输出结果：7
 *std::cout << res2.get() << std::endl;
 */


class ThreadPoolTool {
private:
    //工作线程
    std::vector<std::thread> m_workers;
    //任务队列，存放匿名函数
    std::queue<std::function<void()>> m_tasks;
    //任务队列的互斥锁
    std::mutex m_tasksMutex;
    //条件变量，用来唤醒工作线程
    std::condition_variable m_condition;
    //线程是否在工作
    bool m_stop;

public:
    ThreadPoolTool(size_t size) {
        this->m_stop = false;

        for (size_t i = 0; i < size; i++) {

            //直接在线程数组构造线程,构造线程时，线程就已经开始执行
            this->m_workers.emplace_back(
                [this]() {
                    while (true) {
                        std::function<void()> task;
                        {
                            std::unique_lock<std::mutex> lock(this->m_tasksMutex);

                            //等待条件：线程通过条件变量等待任务到来或停止信号。(CPU使用率：休眠时接近0%，仅在任务到来时唤醒)
                            //lambda表达式作为谓词，当条件(停止信号为true 或 任务队列非空)为真时，才会解除阻塞。
                            this->m_condition.wait(lock, [this]()->bool { return this->m_stop || !this->m_tasks.empty(); });

                            /* 传统忙等待：while (!(stop || !tasks.empty())) {} // 空循环消耗CPU */

                            if (this->m_stop && this->m_tasks.empty()) return;

                            //任务提取：从队列中去除任务执行，使用std::move避免开销
                            task = std::move(this->m_tasks.front());
                            this->m_tasks.pop();

                        }

                        //执行任务
                        task();
                    }
                });
        }
    }


    /**
     * 任务提交
     * @tparam F
     * @tparam ARGS
     * @param f
     * @param args
     * @return
     */
    template<typename F,typename ...ARGS>
    std::future<typename std::result_of<F(ARGS...)>::type> enqueue(F&& f,ARGS&& ...args) {

        //任务封装：使用std::packaged_task包装用户任务，支持异步返回结果
        //智能指针管理：shared_ptr确保任务对象的生命周期延续至执行完毕
        //完美转发：通过std::forward保持参数左值/右值特性
        using returnType = typename std::result_of<F(ARGS...)>::type;
        std::shared_ptr<std::packaged_task<returnType()>> task = std::make_shared<std::packaged_task<returnType()>>(
            std::bind(std::forward<F>(f),std::forward<ARGS>(args)...)
            );
        std::future<returnType> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(this->m_tasksMutex);
            if (this->m_stop) throw std::runtime_error("任务提交停止！");
            this->m_tasks.emplace([task](){(*task)();});

            /* push传入的对象需要事先构造好，再复制过去插入容器中；
              而emplace则可以自己使用构造函数所需的参数构造出对象，并直接插入容器中。
              emplace相比于push省去了复制的步骤，则使用emplace会更加节省内存。*/
        }
        this->m_condition.notify_one();

        return res;




    }

    ~ThreadPoolTool() {
        {
            std::unique_lock<std::mutex> lock(this->m_tasksMutex);
            this->m_stop = true;
        }
        this->m_condition.notify_all();

        for (size_t i= 0; i < this->m_workers.size(); i++) {
            //在析构时调用 join()，确保所有线程正确退出。
            this->m_workers[i].join();
        }


    }


};